//
// Created by zengao on 2022/12/29.
//

#include "CodeGen.h"
#include <cassert>

void C100::CodeGen::visitorProgramNode(C100::ProgramNode *node) {
    printf("\t.text\n");
#ifdef __linux__
    printf("\t.globl prog\n");
    printf("prog:\n");
#endif
    int stackSize = 0;
    for(auto &v : node->localVars){
        stackSize += 8;
        v->offset = stackSize * -1;
    }
    printf("\tpush %%rbp\n");
    printf("\tmov %%rsp,%%rbp\n");
    printf("\tsub $%d,%%rsp\n",stackSize);

    for(auto &s:node->stmts){
        s->accept(this);
        assert(stackLever == 0);
    }
    printf("\tmov %%rbp,%%rsp\n");
    printf("\tpop %%rbp\n");
    printf("\tret\n");
}

void C100::CodeGen::visitorBinaryNode(C100::BinaryNode *node) {
    node->Rhs->accept(this);
    push();
    node->Lhs->accept(this);
    pop("%rdi");

    switch (node->binOp) {
        case BinaryOperator::Sub:
            printf("\tsub %%rdi,%%rax\n");
            break;
        case BinaryOperator::Add:
            printf("\tadd %%rdi,%%rax\n");
            break;
        case BinaryOperator::Mul:
            printf("\timul %%rdi,%%rax\n");
            break;
        case BinaryOperator::Div:
            printf("\tcqo\n");
            printf("\tidiv %%rdi\n");
            break;
        case BinaryOperator::Equal:
            printf("\tcmp %%rdi,%%rax\n");
            printf("\tsete %%al\n");
            printf("\tmovzb %%al,%%rax\n");
            break;
        case BinaryOperator::PipeEqual:
            printf("\tcmp %%rdi,%%rax\n");
            printf("\tsetne %%al\n");
            printf("\tmovzb %%al,%%rax\n");
            break;
        case BinaryOperator::Lesser:
            printf("\tcmp %%rdi,%%rax\n");
            printf("\tsetl %%al\n");
            printf("\tmovzb %%al,%%rax\n");
            break;
        case BinaryOperator::LesserEqual:
            printf("\tcmp %%rdi,%%rax\n");
            printf("\tsetle %%al\n");
            printf("\tmovzb %%al,%%rax\n");
            break;
        case BinaryOperator::Greater:
            printf("\tcmp %%rdi,%%rax\n");
            printf("\tsetg %%al\n");
            printf("\tmovzb %%al,%%rax\n");
            break;
        case BinaryOperator::GreaterEqual:
            printf("\tcmp %%rdi,%%rax\n");
            printf("\tsetge %%al\n");
            printf("\tmovzb %%al,%%rax\n");
            break;
        default:
            assert(0);
            break;
    }
}

void C100::CodeGen::visitorConstantNode(C100::ConstantNode *node) {
    printf("\tmov $%d,%%rax\n", node->value);
}

void C100::CodeGen::push() {
    printf("\tpush %%rax\n");
    stackLever++;
}

void C100::CodeGen::pop(const char *reg) {
    printf("\tpop %s\n", reg);
    stackLever--;
}

void C100::CodeGen::visitorExprStmtNode(ExprStmtNode *node) {
    node->Lhs->accept(this);
}

void C100::CodeGen::visitorAssignExprNode(AssignExprNode *node){
    auto varNode = std::dynamic_pointer_cast<VarExprNode>(node->Lhs);
    assert(varNode != nullptr);
    printf("\tlea %d(%%rbp),%%rax\n",varNode->varObj->offset);
    push();
    node->Rhs->accept(this);
    pop("%rdi");
    printf("\tmov %%rax,(%%rdi)\n");
}

void C100::CodeGen::visitorVarExprNode(VarExprNode *node) {
    printf("\tlea %d(%%rbp),%%rax\n",node->varObj->offset);
    printf("\tmov (%%rax),%%rax\n");
}

void C100::CodeGen::visitorIfStmtNode(C100::IfStmtNode *node) {
    int n =sequence++;
    node->Cond->accept(this);
    printf("\tcmp $0, %%rax\n");
    if(node->Else){
        printf("\tje .L.else_%d\n",n);
    }else{
        printf("\tje .L.end_%d\n",n);
    }

    node->Then->accept(this);
    printf("\tjmp .L.end_%d\n",n);

    if(node->Else){
        printf(".L.else_%d:\n",n);
        node->Else->accept(this);
    }
    printf(".L.end_%d:\n",n);
}

void C100::CodeGen::visitorBlockStmtNode(C100::BlockStmtNode *node) {
    for(auto &s : node->stmts){
        s->accept(this);
    }
}
